Skip to content

NodesList: look nodes up under the same monitor as add and remove - #22

Merged
prabhu merged 1 commit into
mainfrom
fix/nodeslist-concurrent-reads
Oct 25, 2026
Merged

prabhu merged 1 commit into
mainfrom
fix/nodeslist-concurrent-reads

Conversation

@prabhu

@prabhu prabhu commented Oct 25, 2026

Copy link
Copy Markdown
Contributor

atom crashed intermittently with unable to read node from disk while jssrc2cpg's import resolver ran: 9 of 10 usages runs on node-express-realworld-example-app with TypeScript types on.

The cause is in NodesList. contains and nodeById read the trove id index without a lock, while add, remove and compact change it under the list's monitor. A lookup that races an add which rehashes the index can miss a node that is in the list. Pass writers add nodes while the pass's parts traverse the graph, so a NodeRef built from an id, such as chen's OpNodes.Assignment wrapper, could come out empty. Its first property read then went to disk for a node that had never been written there.

The lookups now take the monitor, and so does access to the label map, which readers also changed through computeIfAbsent. The lists returned by nodesByLabel are still live, so iterating them while another thread adds nodes of that label remains unsafe. I haven't seen that fail, and it is left for a separate change.

With the fix, the same project passes 30 of 30 runs with byte-identical output. The other four corpus repos pass 10 of 10 each. juice-shop usages and reachables take the same time as before (11 to 17 s either way) and give identical slices. The new NodesListTest case fails every time without the change. Version bumped to 3.1.1; cpg2 and chen need to follow for atom to pick it up.

contains and nodeById read the trove id index without a lock, while add,
remove and compact change it under the list's monitor. A lookup that races
an add which rehashes the index can miss a node that is in the list. Pass
writers add nodes while the pass's parts traverse the graph, so a NodeRef
built from an id, such as the OpNodes Assignment wrapper, could come out
empty. Its first property read then tried to load the node from disk,
where it had never been written, and failed with "unable to read node
from disk". atom crashed this way in 9 of 10 usages runs on
node-express-realworld-example-app with TypeScript types on.

The lookups now take the monitor, and so does access to the label map,
which readers also changed through computeIfAbsent. The lists handed out
by nodesByLabel are still live and not safe to iterate during concurrent
adds; that is left for a separate change.

With this, the same project passes 30 of 30 runs with byte-identical
usages, and juice-shop usages and reachables take the same time as before.
The new NodesListTest case fails without the change. Bump to 3.1.1.

Signed-off-by: Team AppThreat <cloud@appthreat.com>
@prabhu prabhu added bug Something isn't working ai-auto labels Oct 25, 2026
@prabhu
prabhu enabled auto-merge October 25, 2026 00:45
@prabhu
prabhu added this pull request to the merge queue Oct 25, 2026
Merged via the queue into main with commit d1fc63e Oct 25, 2026
1 check passed
@prabhu
prabhu deleted the fix/nodeslist-concurrent-reads branch October 25, 2026 00:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai-auto bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant